﻿using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using Shell32;
using Baby.Core.Common;
namespace Baby.VideoConvertor
{
    public class VideoHandle
    {
        private readonly string _workingDir;

        private readonly string _programDir;

        private bool _isReady = true;

        readonly Process _proc;
        /// <summary>
        /// 转换工具是否就绪。若工作目录未找到ffmpeg.exe文件则该属性值为false
        /// </summary>
        public bool IsReady
        {
            get { return _isReady; }
        }

        public VideoHandle(string workingDir)
        {
            _workingDir = workingDir;
            _programDir = AppDomain.CurrentDomain.BaseDirectory;
            _proc = new Process();
            _proc.StartInfo.UseShellExecute = true;
            _proc.StartInfo.WorkingDirectory = _programDir;
            _proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
            _proc.StartInfo.FileName = "ffmpeg";
            Ready();
        }

        void Ready()
        {
            if (!File.Exists(Path.Combine(_programDir, "ffmpeg.exe")))
            {
                _isReady = false;
                return;
            }

            if (!string.IsNullOrEmpty(_workingDir) && !Directory.Exists(_workingDir))
            {
                Directory.CreateDirectory(_workingDir);
            }
        }

        public ConvertResult Convert(string inputFile, string outputFile, ref bool isKill)
        {
            if (!_isReady)
            {
                return ConvertResult.Unready;
            }

            //目标文件信息
            var outputFileInfo = new FileInfo(outputFile);
            //目标文件是否存在
            if (outputFileInfo.Exists)
            {
                //如果目标文件正在使用，则认定文件已转换正在移动
                if (IsUsed(outputFile))
                {
                    return ConvertResult.ConvertingByOther;
                }
                //移除同名文件
                File.Delete(outputFile);
            }

            //如果目标目录不存在则创建
            if (outputFileInfo.Directory != null && !outputFileInfo.Directory.Exists)
            {
                Directory.CreateDirectory(outputFileInfo.Directory.FullName);
            }

            //获取源文件信息
            var inputFileInfo = new FileInfo(inputFile);
            //Logo图标
            var logoName = Setting.LogoName;
            //临时文件名
            var globalName = inputFileInfo.Name + ".temp";
            //构建临时文件完全限定路径
            var tempFileName = Path.Combine(_workingDir, globalName + outputFileInfo.Extension);

            //如果有图标文件则视频转换的同时添加Logo标志
            var logoPara = string.Empty;
            if (!string.IsNullOrEmpty(logoName))
            {
                var logoFile = Path.Combine(_programDir, logoName);
                if (File.Exists(logoFile))
                {
                    logoPara = string.Format("-vf \"movie={0}[logo];[in][logo] overlay=10:10:0[out]\"", Setting.LogoName);
                }
            }

            //如果源视频格式与目标视频格式相同，则改先转换avi
            if (string.Equals(inputFileInfo.Extension, outputFileInfo.Extension, StringComparison.InvariantCultureIgnoreCase))
            {
                var flv2AviFileName = Path.Combine(_workingDir, inputFileInfo.Name + ".avi");
                _proc.StartInfo.Arguments = string.Format("-y -i {0} {1}", inputFile, flv2AviFileName);
                _proc.Start();
                _proc.WaitForExit();
                _proc.Close();
                inputFile = flv2AviFileName;
            }

            _proc.StartInfo.Arguments = string.Format("-y -i {0} -s {1} -b:v {2} -q {3} -ar {4} -ab {5} -ac {6} {7} {8}", inputFile,
                            Setting.VideoSize, Setting.VideoBitrate, Setting.VideoQuality,
                            Setting.AudioSamplingFrequency, Setting.AudioBitrate, Setting.AudioChannels, logoPara, tempFileName);
            _proc.Start();
            _proc.WaitForExit();
            _proc.Close();

            return isKill ? ConvertResult.BeStoped : AfterConverted(inputFile, outputFile, tempFileName);
        }

        private ConvertResult AfterConverted(string inputFile, string outputFile, string tempFileName)
        {
            var fInfo = new FileInfo(tempFileName);
            //转换成功
            if (fInfo.Exists && fInfo.Length > 0)
            {
                if (!File.Exists(outputFile))
                {
                    //File.Move(tempFileName, outputFile);
                    File.Copy(tempFileName, outputFile);
                    File.Delete(tempFileName);
                }
                if (File.Exists(inputFile))
                {
                    File.Delete(inputFile);
                }

                if (Setting.CropPosition > 0)
                {
                    var outputFileInfo = new FileInfo(outputFile);
                    var jpgName = outputFileInfo.Name;
                    jpgName = jpgName.Substring(0, jpgName.Length - outputFileInfo.Extension.Length) + ".jpg";
                    jpgName = Path.Combine(outputFileInfo.DirectoryName, jpgName);
                    _proc.StartInfo.Arguments = string.Format("-i {0} -f image2 -ss {1} -s {2} \"{3}\" ", outputFile, Setting.CropPosition, Setting.ImgSize, jpgName);
                    _proc.Start();
                    _proc.WaitForExit();
                    _proc.Close();
                }
                return ConvertResult.Succeed;
            }
            //转换失败
            //获取源文件信息
            var inFileInfo = new FileInfo(inputFile);
            //构建转换失败的视频的新文件名
            var currTime = DateTime.Now;
            var timeName = currTime.ToString("yyMMddHHmmssfff");
            var fileName = string.Format("Eor_{0}_{1}", timeName, inFileInfo.Name);
            //指定存放处理失败的视频的存放目录为工作目录
            var trashDir = _workingDir;
            //检测是否配置了转换失败的视频存放目录
            if (!string.IsNullOrEmpty(Setting.TrashDirectory))
            {
                //获取目录信息
                var trashDirInfo = new DirectoryInfo(Setting.TrashDirectory);
                //如果目录不存在则创建目录
                if (!trashDirInfo.Exists)
                {
                    Directory.CreateDirectory(Setting.TrashDirectory);
                }
                //将失败视频存放目录指定为配置的目录
                trashDir = Setting.TrashDirectory;
            }
            //如果源文件存在则移动到存放失败视频的目录
            var errorFile = Path.Combine(trashDir, fileName);
            if (inFileInfo.Exists)
            {
                //File.Move(inputFile, errorFile);
                inFileInfo.CopyTo(errorFile);
                inFileInfo.Delete();
            }
            //如果存在转换时生成的临时文件则删除
            if (File.Exists(tempFileName))
            {
                File.Delete(tempFileName);
            }
            //发送通知邮件
            if (Setting.EnabledEmailNotice && !string.IsNullOrEmpty(Setting.ToEmail))
            {
                var obj = new MailContract { ToMail = Setting.ToEmail, SourceFile = inputFile, ErrorFile = errorFile };
                var thread = new Thread(SendMail);
                thread.Start(obj);
            }
            return ConvertResult.Error;
        }

        static void SendMail(object obj)
        {
            var m = (MailContract)obj;
            var fileName = new FileInfo(m.SourceFile).Name;
            var mailContext = new MailContext
            {
                Address = Setting.ToEmail,
                Subject = string.Format("视频{0}转换错误通知", fileName),
                Body = string.Format("文件:{0} 在转换时发生错误，原文件已更名并保存到指定路径{1}，请使用手动转换！", m.SourceFile, m.ErrorFile)
            };
            Mail.Instance.SendMail(mailContext);
        }

        static bool IsUsed(string path)
        {
            FileStream fs = null;
            var isUsed = false;
            try
            {
                fs = File.Open(path, FileMode.Append, FileAccess.Write);
            }
            catch
            {
                isUsed = true;
            }

            if (fs != null)
            {
                fs.Close();
            }
            return isUsed;
        }

        ///　<summary>
        ///　获取媒体播放时间长度，格式00:00:00。
        ///　</summary>
        ///　<param　name="path">媒体路径</param>
        ///　<returns>播放时间长度</returns>
        static string GetMediaDuration(string path)
        {
            try
            {
                FileInfo fileInfo = new FileInfo(path);
                Shell shell = new ShellClass();
                Folder folder = shell.NameSpace(fileInfo.DirectoryName);
                FolderItem folderitem = folder.ParseName(fileInfo.Name);

                return folder.GetDetailsOf(folderitem, 21);
            }
            catch (Exception ex)
            {
                return null;
            }
        }
    }

    public enum ConvertResult
    {
        /// <summary>
        /// 正在被其他线程转换
        /// </summary>
        ConvertingByOther,

        /// <summary>
        /// 转换完成
        /// </summary>
        Succeed,

        /// <summary>
        /// 转换出错
        /// </summary>
        Error,

        /// <summary>
        /// 程序未就绪，可能是缺少ffmpeg.exe工具
        /// </summary>
        Unready,

        /// <summary>
        /// 转换被终止
        /// </summary>
        BeStoped
    }

    public class MailContract
    {
        public string ToMail { get; set; }

        public string SourceFile { get; set; }

        public string ErrorFile { get; set; }
    }
}